package destiny.预测;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import destiny.干支;
import destiny.干支局;
import destiny.common.阴阳;
import destiny.五行.五行;
import destiny.干.十神;
import destiny.干.天干;
import destiny.干.天干五合;
import destiny.支.地支六冲;
import destiny.支.地支相刑;
import destiny.支.地支相害;
import destiny.神煞.用神煞;
import destiny.神煞.神煞;

public class 预测论断 {

	private 干支数[] 四柱数;
	private 运神 用神;
	private 格局 命格;
	private boolean 乾造;

	private String 观局(四柱排盘 pan, 成局 ju) {
		干支数[] 柱数 = pan.柱数();
		StringBuilder sb = new StringBuilder();
		boolean first = true;
		for (int idx : ju.getIndexes()) {
			if (first) {
				sb.append(String.format("%s(%s) \t: ", ju.局(), 干支局.天干五合
						.equals(ju.局()) ? ((天干五合) ju.getEnumInstance()).五行()
						: ju.getEnumInstance()));
				first = false;
			}
			sb.append(pan.柱名(idx));
			if (ju.局().干局()) {
				sb.append(String.format("干 - %s\t", 柱数[idx].干数().天干()));
			} else {
				sb.append(String.format("支 - %s\t", 柱数[idx].支数().地支()));
			}
		}
		return sb.toString();
	}

	private void 观局(四柱排盘 pan) {
		System.out.println("== 成局 ==");
		for (成局 ju : pan.局集()) {
			if (Consts._VERBOSE) {
				System.out.println(String.format("\t\t%s", 观局(pan, ju)));
			}
			if (干支局.天干五合.equals(ju.局())) {
				/*
				 * 天干合化为真者，富贵至名公巨卿。合化为假者，则为孤儿异性或为僧道。干合者，有早婚之兆。如下为方便学习者作备考查验用，
				 * 一因四柱不全不能盖论， 二因古人对十干性质有细微的讲究故有待于在实践中一一论证。
				 * 
				 * 甲己合化土：为中正之合。主安分守己、重信讲义。若命局无它土，又带七杀，则缺乏情义、诡计多端、不知廉耻，性刚。
				 * 
				 * 甲日干合己，遇乙木：妻财暗损；丁火：衣禄成空；辛金：贵显高门；戊土：家殷大富；癸水：平生发福；庚金：家徒四壁；丙火：
				 * 禄享千钟。
				 * 
				 * 己日合甲干，遇丁火：他人凌辱；乙木：自己遭遇；辛金：家殷巨富；庚金：孤寒白屋；癸水：官职迁荣。
				 * 
				 * 乙庚合化金：主仁义之合。刚柔兼备，重仁守义。若有偏官或坐死绝等弱运者，反固执己见，轻仁寡义。
				 * 
				 * 乙日干合庚，遇丙火：蹇难；癸水：荣华；丁火：似春花之笑日；己土：满堂金玉；辛金：若秋草逢霜；甲木：麻麦盈仓。
				 * 
				 * 庚日干合乙，遇金：暗损；丙火：相煎；丁火：如蛟龙得云雨；癸水：田园漂荡；壬水：财禄增迁；戊土：不成巨富，逢壬水助方永保长年
				 * 。
				 * 
				 * 丙辛合化水：主威严之合。仪表威严，智力优秀。若带七杀或坐死绝者，反性酷无情，乖僻寡合。女命逢支冲，合化之水，主性感纵欲 。
				 * 
				 * 丙日干合辛，遇戊土：成名；乙木：官爵迁荣；癸水己土：家门显赫；壬水辰土：祸败。
				 * 
				 * 辛日干合丙，遇戊土庚金：功名。
				 * 
				 * 丁壬合化木：主仁寿之合。心地仁慈，长命多寿。妇命若命局水过旺泄木，则为淫欲之合。若坐死绝者，酒色破家。
				 * 
				 * 丁日干合壬，遇丙火：历年安逸；辛金：一进优游，富贵双全；戊土：活计消遣；癸水：生涯寂寞；乙木重重：财禄无成；庚金叠叠 ：
				 * 功名莫望； 喜甲临辰： 禄封双美；喜已共酉亦禄封双美。
				 * 
				 * 壬日干合丁，遇甲木：多遭仆马；辛金：广置田庄；丙火：英雄豪杰；癸水：辛苦经商；己土：佩印乘轩；戊土：飘蓬落魄；庚金：
				 * 皓首无成；乙木 ： 青年不遇。
				 * 
				 * 戊癸合化火：主无情之合。相貌俊秀，薄情乏义，男多抱玩世之心，女则多嫁俊夫。
				 * 
				 * 戊日干合癸，遇乙木：终能显达；壬水：独自丰隆；丙火：难寻福禄；庚金：易见亨通；己土：妻子有损；辛金：谋略为拙。
				 * 
				 * 癸日干合戊，遇丙辛：一世多成多败；甲已：历年劳心劳力；丁火：仓库丰肥；庚金：田财殷实；乙木：官爵陆荣；壬水：，财禄两全 ；
				 * 辛金： 财缘得失； 己土：仕途蹭蹬。
				 */
				System.out.println(String.format("\t%s",
						((天干五合) ju.getEnumInstance()).getMessage()));
			} else if (干支局.冲.equals(ju.局())) {
				System.out.println(String.format("\t%s",
						((地支六冲) ju.getEnumInstance()).getMessage()));
			} else if (干支局.刑.equals(ju.局())) {
				System.out.println(String.format("\t%s",
						((地支相刑) ju.getEnumInstance()).getMessage()));
			} else if (干支局.害.equals(ju.局())) {
				System.out.println(String.format("\t%s",
						((地支相害) ju.getEnumInstance()).getMessage()));
			}
		}
	}

	private void 天干运神(四柱排盘 pan) {
		System.out.println("== 天干运神 ==");
		for (int i = 0; i < pan.柱数().length; ++i) {
			干数 干数 = pan.柱数()[i].干数();
			System.out.print(String.format(
					"\t%s\t%s(%s):\t%s%s %d\t%s 支藏 :",
					pan.柱数()[i].干支().纳音(),

					干数.天干(),
					干数.术数().五行(),
					i == pan.日序 ? "" : 干数.神(),
					i == pan.日序 ? "(主)" : 干数.用神权重() == 5 ? "(用)"
							: 干数.用神权重() == 4 ? "(喜)" : 干数.术数().五行()
									.equals(用神.忌神()) ? "(忌)" : "", 干数
							.getWeight(),

					pan.柱数()[i].支数().地支()));

			for (干数 数 : pan.柱数()[i].支数().藏干数()) {
				System.out.print(String.format("\t- %s\t%s%s\t%d", 数.术数().五行(),
						数.神(), 数.用神权重() == 5 ? "(用)" : 数.用神权重() == 4 ? "(喜)"
								: 数.术数().五行().equals(用神.忌神()) ? "(忌)" : "", 数
								.getWeight()));
			}
			System.out.println();
		}
	}

	public 预测论断(干支[] 柱, boolean 乾造) {
		this.乾造 = 乾造;

		四柱排盘 pan = new 四柱排盘(柱);
		四柱数 = pan.柱数();

		pan.运盘();

		用神 = 运神.日干求神(四柱数);
		Utils.用神(用神, 四柱数);

		命格 = 格局.求格局(pan);

		try {
			用神煞.定神煞(四柱数, pan.年序, pan.月序, pan.日序);
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("== 四柱 ==");
		System.out.println(String.format("\t年 : %s; 月 : %s; 日 : %s; 时 : %s",
				柱[0], 柱[1], 柱[2], 柱[3]));

		System.out.println("== 用神喜忌 ==");
		System.out.println(String.format("\t用神：%s", 用神.用神()));
		System.out.println(String.format("\t喜神：%s", 用神.喜神()));
		System.out.println(String.format("\t忌神：%s", 用神.忌神()));

		观局(pan);

		if (Consts._VERBOSE) {
			天干运神(pan);

			System.out.println("== 格局 ==");
			System.out.println(String.format("\t%s", 命格.取格()));
			System.out.println(String.format("\t%s局", 命格.十神格()));
		}

		观神煞(pan);
	}

	private void 观神煞(四柱排盘 pan) {
		System.out.println("== 神煞 ==");
		Set<String> strs = new HashSet<String>();
		for (int i = 0; i < 四柱数.length; ++i) {
			for (神煞 ss : 四柱数[i].神煞()) {
				if (Consts._VERBOSE) {
					System.out.println(String.format("\t%s柱 : %s :\t%s", pan
							.柱名(i), ss.用神煞().getName(), ss.用神煞().getMessage()));
				} else {
					strs.add(ss.用神煞().getMessage());
				}
			}
			数 数 = 四柱数[i].干数();
			if (数.神煞().size() > 0) {
				if (Consts._VERBOSE) {
					System.out.println(String.format("\t%s干 -", pan.柱名(i)));
				}
				for (神煞 ss : 数.神煞()) {
					if (Consts._VERBOSE) {
						System.out.println(String.format("\t\t%s%s : %s :\t%s",
								pan.柱名(ss.主柱序()), ss.主干() ? "干" : "支", ss.用神煞()
										.getName(), ss.用神煞().getMessage()));
					} else {
						strs.add(ss.用神煞().getMessage());
					}
				}
			}

			数 = 四柱数[i].支数();
			if (数.神煞().size() > 0) {
				if (Consts._VERBOSE) {
					System.out.println(String.format("\t%s支 -", pan.柱名(i)));
				}
				for (神煞 ss : 数.神煞()) {
					if (Consts._VERBOSE) {
						System.out.println(String.format("\t\t%s%s : %s :\t%s",
								pan.柱名(ss.主柱序()), ss.主干() ? "干" : "支", ss.用神煞()
										.getName(), ss.用神煞().getMessage()));
					} else {
						strs.add(ss.用神煞().getMessage());
					}
				}
			}
		}
		for (String s : strs)
			System.out.println(String.format("\t%s", s));
	}

	public void 综合命评() {
		System.out.println("== 心性 ==");
		System.out.println(String.format("\t%s", 命格.十神格().心性()));

		// 阴阳互通而五行易失和
		Map<五行, Integer> weights = Utils.五行用神数(四柱数);
		int average = 0, weight;
		for (五行 ele : weights.keySet()) {
			weight = weights.get(ele);
			average += weight;
		}
		average /= weights.size();

		StringBuilder 外貌 = new StringBuilder(), 健康 = new StringBuilder();
		// 五行最强显于外，日主强弱为内，五行不平衡影响即为健康隐患
		int deviation = 0;
		for (五行 ele : weights.keySet()) {
			weight = weights.get(ele);
			deviation += (weight - average) * (weight - average);
		}
		deviation /= weights.size();
		deviation = (int) Math.sqrt(deviation);
		for (五行 ele : weights.keySet()) {
			weight = weights.get(ele);
			if (Math.abs(weight - average) > deviation) {
				if (weight > average) {
					外貌.append(String.format("\t%s\n", ele.盛者外貌()));
					if (weight > average * 2)
						System.out.println(String.format("\t%s", ele.过盛人事()));
					else
						System.out.println(String.format("\t%s", ele.盛者人事()));
				} else {
					外貌.append(String.format("\t%s\n", ele.衰者外貌()));
					System.out.println(String.format("\t%s", ele.衰者人事()));
				}
				天干 x = 天干.lookup(ele, 阴阳.阴), y = 天干.lookup(ele, 阴阳.阳);
				健康.append(String.format("\t%s、%s容易病变，日常注意%s、%s的不适和变化。\n",
						x.脏腑(), y.脏腑(), x.人体(), y.人体()));
			}
		}
		System.out.println("== 外貌 ==");
		System.out.println(外貌.toString());

		System.out.println("== 健康 ==");
		System.out.println(健康.toString());

		System.out.println("== 家宅工作 ==");
		五行 ele = 用神.用神();
		System.out.println(String.format("\t宜往%s的方向定居，或者从事%s方面的职业", ele.方位(),
				ele));
		ele = 用神.喜神();
		System.out.println(String.format("\t或者往%s的方向定居，或者从事%s方面的职业", ele.方位(),
				ele));
		ele = 用神.忌神();
		System.out.println(String.format("\t注意不宜往%s的方向定居，或者从事%s方面的职业",
				ele.方位(), ele));

		System.out.println("== 家宅人事 ==");
		干数 x = 四柱数[Consts.四柱日序].干数(), y;
		System.out.println(String.format("\t命主先天身体%s。", 强弱(x.getWeight())));
		int i = Consts.四柱年序, w = 0;
		y = 四柱数[i].干数();
		w = y.getWeight();
		System.out.println(String.format("\t%s%s。", 四柱排盘.宫名2(i, true, 乾造),
				强弱(w)));
		y = 四柱数[i].支数().藏干数()[0];
		w = y.getWeight();
		if (i == Consts.四柱月序) {
			w = w * Consts.四柱权重 / Consts.月提权重;
		}
		System.out.println(String.format("\t%s%s。", 四柱排盘.宫名2(i, false, 乾造),
				强弱(w)));
	}

	public 干支数[] 流年行运(干支[] 流年行运, int 大运经年) {
		/**
		 * 0 - 运柱; 1 - 流年柱
		 * 
		 * 2 - 年柱; 3 - 月柱; 4 - 日柱; 5 - 时柱
		 */
		干支[] 柱 = { 流年行运[0], 流年行运[1], 四柱数[0].干支(), 四柱数[1].干支(), 四柱数[2].干支(),
				四柱数[3].干支() };

		流年行运排盘 pan = 流年行运排盘.运盘(柱, 大运经年);
		pan.运盘();

		Utils.用神(用神, pan.柱数());

		if (Consts._VERBOSE) {
			观局(pan);

			天干运神(pan);
		}
		return pan.柱数();
	}

	private final static int DAMN_LT = 0;
	private final static int LT = 1;
	private final static int SAME = 2;
	private final static int GT = 3;
	private final static int DAMN_GT = 4;

	/**
	 * 星评，论财印官伤食比劫
	 */
	public String 流年行运星评(干支数[] gzs, int index, boolean 干) {
		干数 gs;
		int weight;
		if (干) {
			gs = gzs[index].干数();
			weight = gs.getWeight();
		} else {
			// FIXME: 只看主气，余气不论
			干数[] gss = gzs[index].支数().藏干数();
			gs = gss[0];
			weight = (int) (gs.getWeight() / Consts.藏干权重[gss.length - 1][0]);
			if (index == Consts.流年行运月序) {
				weight = weight * Consts.四柱权重 / Consts.月提权重;
			}
		}

		return 神变命评(日元权重比较(gzs, weight), gs.神());
	}

	private int 日元权重比较(干支数[] gzs, int weight) {
		int mainWeight = gzs[Consts.流年行运日序].干数().getWeight();
		if (Math.abs(mainWeight - weight) < Math.min(强弱[getLevel(mainWeight)],
				mainWeight) / 2)
			return SAME;
		if (weight < mainWeight) {
			if (命宫大动(mainWeight, weight))
				return DAMN_LT;
			else
				return LT;
		} else {
			if (命宫大动(mainWeight, weight))
				return DAMN_GT;
			else
				return GT;
		}
	}

	/**
	 * 宫评，以父母，夫妻为主，求子女，不论财印官伤食比劫
	 */
	public String 流年行运宫评(干支数[] gzsOld, 干支数[] gzs, int index, boolean 干,
			String prefix) {
		int w1, w2;
		if (干) {
			w1 = gzsOld[index].干数().getWeight();
			w2 = gzs[index].干数().getWeight();
		} else {
			// FIXME: 和值入宫，不论五行之气
			w1 = w2 = 0;
			for (干数 gs : gzsOld[index].支数().藏干数()) {
				w1 += gs.getWeight();
			}
			for (干数 gs : gzs[index].支数().藏干数()) {
				w2 += gs.getWeight();
			}
			if (index == Consts.流年行运月序) {
				w1 = w1 * Consts.四柱权重 / Consts.月提权重;
				w2 = w2 * Consts.四柱权重 / Consts.月提权重;
			}
		}
		boolean bigChange = 命宫大动(w1, w2);

		if (index == Consts.流年行运时序) {
			if (bigChange && w1 > w2)
				return String.format("可能受孕得%s。", prefix);
			else
				return "";
		}

		StringBuilder sb = new StringBuilder();
		sb.append(成局变化(gzsOld[index], gzs[index], 干, index == Consts.流年行运日序));
		if (bigChange) {
			sb.append("可能有灾劫，注意身体和可能发生的意外！");
		}
		return "".equals(sb.toString()) ? "" : String.format("%s（宫）: %s",
				prefix, sb.toString());
	}

	private String 成局变化(干支数 gzsOld, 干支数 gzs, boolean 干, boolean 夫妻宫) {
		StringBuilder sb = new StringBuilder();
		成局[] cj = {};
		// 成局改变
		if (干) {
			sb.append(局数变化(gzsOld.干数(), gzs.干数()));
			cj = gzs.干数().成局(干支局.天干五合);
		} else {
			sb.append(局数变化(gzsOld.支数(), gzs.支数()));
			cj = gzs.支数().成局(干支局.地支六合);
		}

		// 夫妻和睦
		if (夫妻宫) {
			boolean third = false;
			for (成局 c : cj) {
				for (int i : c.getIndexes()) {
					if (c.数().get(i).成局(干 ? 干支局.天干五合 : 干支局.地支六合).length > 1) {
						third = true;
						break;
					}
				}
				if (third)
					break;
			}
			if (third) {
				if (干)
					sb.append("可能遇到第三者。如单身，可能找到对象。");
				else
					sb.append("可能遇到第三者。");
			}
		}

		return sb.toString();
	}

	private String 局数变化(数 x, 数 y) {
		List<成局> removed = new ArrayList<成局>(), added = new ArrayList<成局>();
		for (成局 i : x.成局()) {
			boolean matched = false;
			for (成局 j : y.成局()) {
				if (i.matched(j)) {
					matched = true;
					break;
				}
			}
			if (!matched)
				removed.add(i);
		}
		for (成局 i : y.成局()) {
			boolean matched = false;
			for (成局 j : x.成局()) {
				if (i.matched(j)) {
					matched = true;
					break;
				}
			}
			if (!matched)
				added.add(i);
		}

		Set<String> ss = new HashSet<String>();
		for (成局 i : removed) {
		}
		for (成局 i : added) {
			if (i.局().equals(干支局.冲)) {
				ss.add(String.format("%s。",
						((地支六冲) i.getEnumInstance()).getRealtimeMessage()));
			}
		}
		StringBuilder sb = new StringBuilder();
		for (String s : ss) {
			sb.append(s);
		}

		return sb.toString();
	}

	private boolean 命宫大动(int w1, int w2) {
		int min = Math.min(w1, w2), max = Math.max(w1, w2);
		int l1 = getLevel(max), l2 = getLevel(min);
		boolean ret = false;
		switch (l2) {
		case 0:
			ret = ((l1 > 0) && (max > min * 2));
			break;
		default:
			ret = ((l1 - l2 > 1) || (max > min * 3));
		}
		if (ret)
			return ((w2 < w1 && l2 < 2) || (w2 > w1 && l1 > 2));
		return false;
	}

	private static int[] 强弱 = { 30, 90, 180, 240 };

	private int getLevel(int weight) {
		for (int i = 0; i < 强弱.length; ++i) {
			if (weight < 强弱[i])
				return i;
		}
		return 强弱.length;
	}

	private String 神变命评(int status, 十神 x) {
		String 神 = null;
		if (十神.正官.equals(x)) {
			神 = 十神指事[0];
		} else if (十神.七杀.equals(x)) {
			神 = 十神指事[1];
		} else if (十神.正印.equals(x) || 十神.偏印.equals(x)) {
			神 = 十神指事[2];
		} else if (十神.正财.equals(x)) {
			神 = 十神指事[4];
		} else if (十神.偏财.equals(x)) {
			神 = 十神指事[5];
		} else if (十神.食神.equals(x)) {
			神 = 十神指事[6];
		} else if (十神.伤官.equals(x)) {
			神 = 十神指事[7];
		} else if (十神.比肩.equals(x)) {
			神 = 十神指事[8];
		} else if (十神.劫财.equals(x)) {
			神 = 十神指事[9];
		} else {
			return "";
		}
		return String.format(旺衰模板[status], 神);
	}

	private static String[] 旺衰模板 = {

	"%s方面可能遇到巨大阻碍，甚至可能前功尽弃，做好心理准备，明哲保身为上。",

	"%s方面可能遇到阻碍，小心对待。",

	"%s方面正常的发展。",

	"有%s方面的机会，注意把握，但要小心物极必反。",

	"在%s方面有非常好的机会，但是物极必反，可能会引起自身的厄运，谨慎对待。",

	};

	private static String[] 十神指事 = {

	"仕途晋升",

	"权力收获",

	"事业/学业", "",

	"个人财富（明财）",

	"个人财富（暗财）",

	"由于自身外貌（情商、巧手）而持续收获（如财富）",

	"由于聪明才智而持续收获（如财富）",

	"朋友帮助",

	"损友（或竞争对手），可能持续败财",

	};

	private String 强弱(int weight) {
		int level = getLevel(weight);
		switch (level) {
		case DAMN_LT:
			return "极弱";
		case LT:
			return "弱";
		case SAME:
			return "正常";
		case GT:
			return "强";
		default:
			return "极强";
		}
	}

	private static String[][] 十神主事 = { { "仕途", "仕途发展", "正大光明，对命主具约身引善之能" },
			{ "权力", "权力发展", "足智多谋，自视权威" }, { "事业/学业", "事业/学业发展", "对命主有教导之意" },
			{ "财富", "财富发展", "为命主财富后盾" }, { "技术能力", "技术能力发展", "靠聪明才智生财，做官则不利" },
			{ "小聪明", "依靠小聪明发展", "靠聪明才智为业，自以为是，做官则不利" },
			{ "朋友", "朋友帮助", "同我助我，异性缘差" },
			{ "损友或竞争对手", "损友或竞争对手开始发力", "损友，败命主之财甚多" }, };
}
